// $codepro.audit.disable
/* Copyright (c) 2011 Team A CS2340. All rights reserved.*/

package edu.gatech.teama.doctorsoffice.client.presenter;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;



import edu.gatech.teama.doctorsoffice.model.Appointment;
import edu.gatech.teama.doctorsoffice.model.Patient;
import edu.gatech.teama.doctorsoffice.model.User;
import edu.gatech.teama.doctorsoffice.net.AddAppointmentMessage;
import edu.gatech.teama.doctorsoffice.net.AddUserMessage;
import edu.gatech.teama.doctorsoffice.net.ChangePasswordMessage;
import edu.gatech.teama.doctorsoffice.net.DeleteUserMessage;
import edu.gatech.teama.doctorsoffice.net.GetAppointmentTimesForDateMessage;
import edu.gatech.teama.doctorsoffice.net.GetDaysAppointmentsMessage;
import edu.gatech.teama.doctorsoffice.net.LoginMessage;
import edu.gatech.teama.doctorsoffice.net.LoginResult;
import edu.gatech.teama.doctorsoffice.net.LookupAllPatientsAppointmentsMessage;
import edu.gatech.teama.doctorsoffice.net.LookupAppointmentMessage;
import edu.gatech.teama.doctorsoffice.net.LookupUsersOfTypeMessage;
import edu.gatech.teama.doctorsoffice.net.Message;
import edu.gatech.teama.doctorsoffice.net.QuitMessage;
import edu.gatech.teama.doctorsoffice.net.ReadUserMessage;
import edu.gatech.teama.doctorsoffice.net.UpdateAppointmentMessage;
import edu.gatech.teama.doctorsoffice.net.UpdateUserMessage;

/**
 * server connection
 * @author unknown
 * @version 1.0
 */
public class ServerConnection {
	/**
	 * server connection instance = null
	 */
	private static ServerConnection Instance = null;
	/**
	 * client socket
	 */
	private Socket clientSocket;
	/**
	 * object output stream
	 */
	private ObjectOutputStream out;
	/**
	 * object input stream in
	 */
	private ObjectInputStream in;
	/**
	 * User logged in user
	 */
	private User loggedInUser;
	/**
	 * serverconnection
	 * @param address
	 * @param port
	 */
	protected ServerConnection(String address, int port) {
		// prevent instantiation

		try {
			clientSocket = new Socket(address, port); // $codepro.audit.disable closeWhereCreated
			out = new ObjectOutputStream(clientSocket.getOutputStream()); // $codepro.audit.disable closeWhereCreated
			out.flush();
			in = new ObjectInputStream(clientSocket.getInputStream()); // $codepro.audit.disable closeWhereCreated
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


	}

	/**
	 * LoginResult login
	 * @param username
	 * @param password
	 * @return null
	 */
	public LoginResult login(String username, String password) {
		Message resultMessage = null;
		Message userMessage = null;

		try {
			out.writeObject(new LoginMessage(username, password));
			resultMessage = (Message) in.readUnshared();
			if ((LoginResult) resultMessage.getPayload() == LoginResult.VALID) {
				out.writeObject(new ReadUserMessage(username));
				userMessage = (Message) in.readUnshared();
				loggedInUser = (User) userMessage.getPayload();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (resultMessage != null) {
			return (LoginResult) resultMessage.getPayload();
		}
		return null;

	}

	/**
	 * get logged in user
	 * @return loggedInUser
	 */
	public User getLoggedInUser() {
		return loggedInUser;
	}

	/**
	 * server connection get instance
	 * @return Instance
	 */
	public static ServerConnection getInstance() {
		if(Instance == null) {
			Instance = new ServerConnection("localhost", 4123);
		}
		return Instance;
	}

	/**
	 * disconnect
	 */
	public void disconnect() {
		try {
			out.writeObject(new QuitMessage());
			out.close(); // $codepro.audit.disable closeInFinally
			in.close(); // $codepro.audit.disable closeInFinally
			clientSocket.close(); // $codepro.audit.disable closeInFinally

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		ServerConnection.Instance = null; // $codepro.audit.disable assignmentToNonFinalStatic
	}

	/**
	 * get output stream
	 * @return out
	 */
	public ObjectOutputStream getOutputStream() {
		return out;
	}

	/**
	 * get input stream
	 * @return in
	 */
	public ObjectInputStream getInputStream() {
		return in;
	}

	/**
	 * lookup appointment
	 * @param key
	 * @return null
	 */
	public Appointment lookupAppointment(String key) {
		Message resultMessage = null;

		try {
			out.writeObject(new LookupAppointmentMessage(key));
			resultMessage = (Message) in.readUnshared();
			return (Appointment) resultMessage.getPayload();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * 
	 * @param date should be formatted as mmddyyyy
	 * @return null
	 */
	public Map<String,Appointment> getAppointmentsForDate(String date) {
		Message resultMessage = null;

		try {
			if (out == null)
				return null;

			out.writeObject(new GetDaysAppointmentsMessage(date));
			resultMessage = (Message) in.readUnshared();
			return (Map<String,Appointment>) resultMessage.getPayload();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * get users of types
	 * @param types
	 * @return users
	 */
	public List<User> getUsersOfTypes(String... types) {
		Message resultMessage = null;
		List<User> users = new ArrayList<User>();

		try {
			for (String type : types) {
				out.writeObject(new LookupUsersOfTypeMessage(type));
				out.flush();
				resultMessage = (Message) in.readUnshared();
				users.addAll((List<User>) resultMessage.getPayload());

			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return users;
	}

	/**
	 * get appointment for user
	 * @param username
	 * @return appointmentList
	 */
	public List<Appointment> getAppointmentForUser(String username){
		Message resultMessage=null;
		List<Appointment> appointmentList = new ArrayList<Appointment>();
		try {
			out.writeObject(new LookupAllPatientsAppointmentsMessage(username));
			resultMessage = (Message) in.readUnshared();
			appointmentList.addAll((List<Appointment>) resultMessage.getPayload());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return appointmentList;
	}

	public void addAppointment(Patient patient, Appointment appointment) {
		Message resultMessage = null; // $codepro.audit.disable variableUsage

		try {
			out.writeObject(new AddAppointmentMessage(patient, appointment));
			resultMessage = (Message) in.readUnshared();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		patient.getAssociatedAppointmentIDs().add(appointment.getID());

		this.updateUser(patient);
	}

	public void updateAppointment(Appointment appointment) {
		Message resultMessage = null; // $codepro.audit.disable variableUsage

		try {
			out.writeObject(new UpdateAppointmentMessage(appointment));
			resultMessage = (Message) in.readUnshared();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * add user
	 * @param u
	 * @param password
	 */
	public void addUser(User u, String password) {
		Message resultMessage = null; // $codepro.audit.disable variableUsage

		try {
			out.writeObject(new AddUserMessage(u, password));
			resultMessage = (Message) in.readUnshared();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public List<LocalDateTime> getBusyTimesForDate(String date) {
		Message resultMessage = null;
		List<LocalDateTime> busyTimesList = new ArrayList<LocalDateTime>();

		try {
			out.writeObject(new GetAppointmentTimesForDateMessage(date));
			resultMessage = (Message) in.readUnshared();
			busyTimesList.addAll((List<LocalDateTime>) resultMessage.getPayload());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return busyTimesList;
	}

	public LocalTime getOfficeOpenTime() {
		return new LocalTime(8, 0, 0);
	}

	public LocalTime getOfficeCloseTime() {
		return new LocalTime(17, 0, 0);
	}

	/**
	 * 
	 * @return in millis
	 */
	public long getDefaultAppointmentDuration() {
		// 1 hour
		return 3600000;
	}

	public void updateUser(User u) {
		Message resultMessage = null;

		try {
			out.writeObject(new UpdateUserMessage(u));
			resultMessage = (Message) in.readUnshared();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public User getUser(String username) {
		Message resultMessage = null;

		try {
			out.writeObject(new ReadUserMessage(username));
			resultMessage = (Message) in.readUnshared();
			return (User) resultMessage.getPayload();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	public void deleteUser(String username) {
		Message resultMessage = null;

		try {
			out.writeObject(new DeleteUserMessage(username));
			resultMessage = (Message) in.readUnshared();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void changePassword(User user, String newPassword) {
		Message resultMessage = null;

		try {
			out.writeObject(new ChangePasswordMessage(user, newPassword));
			resultMessage = (Message) in.readUnshared();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
